/*
 * Decoder
 *
 *  Created on: 10/06/2011
 *      Author: Agustina I. Stekolschik & Ezequiel Palumbo
 */
#include <Decoder.h>
#include <Logging.h>

using logging::Logger;
using logging::LoggerFactory;
using logging::Level;


namespace decoder{
//Split the integer
const unsigned int g_FirstQuarter = 0x20000000;
const unsigned int g_ThirdQuarter = 0x60000000;
const unsigned int g_Half         = 0x40000000;


ArithmeticDecoder::ArithmeticDecoder(){
	this->lowBound = 0;
	this->topBound = 0x7FFFFFFF;
	this->currScale = 0;
	this->currStep = 0;
	this->buffer = 0;
}

DataUnit* ArithmeticDecoder::decodeNext(AbstractContext& currentContext){
	Logger& theRootLogger = LoggerFactory::getInstance().getRootLogger();
	theRootLogger.log(logging::TRACE, "Starting the decoding process.");

	unsigned int value = this->decodeTarget(currentContext.getTotalFrequency());

	unsigned int lowBoundary = 0;
	bool found = false;
	ContextUnit* currentUnit;
	unsigned int position;
	for(position = 0; (position < (currentContext.getUnits()).size() && !found); position++ ){
		currentUnit = currentContext.getUnits().at(position);
		if ((lowBoundary + currentUnit->getFrequency()) <= value){
			lowBoundary += currentUnit->getFrequency();
		}else{
			found = true;
		}
	}


	this->decode(lowBoundary, lowBoundary + currentUnit->getFrequency());
	currentUnit->updateFrequency();

	theRootLogger.log(logging::TRACE, "Finishing the decoding process.");
	return this->dataUnitFactory->createDataUnitFor(currentUnit->getDataUnit().asInt());
}

void ArithmeticDecoder::setDataInput(BitDataInput& dataInput){
	this->bitDataInput = &dataInput;
	this->decodeStart();
}

void ArithmeticDecoder::setDataUnitFactory(DataUnitFactory& dataUnitFactory){
	this->dataUnitFactory = &dataUnitFactory;
}

void ArithmeticDecoder::decodeStart()
{
	Logger& theRootLogger = LoggerFactory::getInstance().getRootLogger();
	theRootLogger.log(logging::TRACE, "Add the first 31 bits to the buffer to stard decoding.");
	for( int i=0; i<31; i++ )
		this->buffer = ( this->buffer << 1 ) | getBit();
}

unsigned int ArithmeticDecoder::decodeTarget( const unsigned int total ){
	Logger& theRootLogger = LoggerFactory::getInstance().getRootLogger();
	theRootLogger.log(logging::TRACE, "Get the current step");
	this->currStep = ( this->topBound - this->lowBound + 1 ) / total;
	theRootLogger.log(logging::TRACE, "Obtain the value");
	return ( this->buffer - this->lowBound ) / this->currStep;
}

void ArithmeticDecoder::decode( const unsigned int low_count, const unsigned int high_count){
	Logger& theRootLogger = LoggerFactory::getInstance().getRootLogger();
	theRootLogger.log(logging::TRACE, "Update bounds to continue decoding");
	this->topBound = this->lowBound + this->currStep * high_count - 1;
	this->lowBound = this->lowBound + this->currStep * low_count;

	theRootLogger.log(logging::TRACE, "Update buffer.");
	while( ( this->topBound < g_Half ) || ( this->lowBound >= g_Half ) ){
		if( this->topBound < g_Half ){
			this->lowBound = this->lowBound * 2;
			this->topBound = this->topBound * 2 + 1;
			this->buffer = 2 * this->buffer + getBit();
		}else if( this->lowBound >= g_Half ){
			this->lowBound = 2 * ( this->lowBound - g_Half );
			this->topBound = 2 * ( this->topBound - g_Half ) + 1;
			this->buffer = 2 * ( this->buffer - g_Half ) + getBit();
		}
		this->currScale = 0;
	}
	theRootLogger.log(logging::TRACE, "Update buffer");
	while( ( g_FirstQuarter <= this->lowBound ) && ( this->topBound < g_ThirdQuarter ) ){
		this->currScale++;
		this->lowBound = 2 * ( this->lowBound - g_FirstQuarter );
		this->topBound = 2 * ( this->topBound - g_FirstQuarter ) + 1;
		this->buffer = 2 * ( this->buffer - g_FirstQuarter ) + getBit();
	}
}

unsigned char ArithmeticDecoder::getBit(){
	try{
		return this->bitDataInput->getNextBit() ? 1 : 0 ;
	}catch (EndOfDataException& e){
		return 0;
	}
}


ArithmeticDecoder::~ArithmeticDecoder(){

}

}
